use crate::manifest::ContributeDefinition;
use crate::services::event_manager::DynamicContributesAddedEvent;
use crate::services::{IContributionKeywordImpl, IContributionManager};
use crate::system::Services;
use parking_lot::RwLock;
use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;

pub struct ContextManager {}

impl ContextManager {
    pub fn new() -> Self {
        Self {}
    }
}

pub type Disposer = Box<dyn FnOnce() + Send>;

#[derive(Clone)]
pub struct ExpansionContext {
    pub plugin_name: String,
    services: Arc<Services>,
    disposers: Arc<RwLock<Vec<Disposer>>>,
    apis: Arc<RwLock<HashMap<String, Box<dyn Any + Send + Sync>>>>,
}

impl ExpansionContext {
    pub fn new(
        plugin_name: String,
        services: Arc<Services>,
        disposers: Arc<RwLock<Vec<Disposer>>>,
        apis: Arc<RwLock<HashMap<String, Box<dyn Any + Send + Sync>>>>,
    ) -> Self {
        Self { plugin_name, services, disposers, apis }
    }

    pub fn with_extension<T: Any, F, R>(&self, name: &str, f: F) -> Option<R>
    where
        F: FnOnce(&T) -> R,
    {
        self.apis.read().get(name).and_then(|api| api.downcast_ref::<T>()).map(f)
    }

    pub fn register_contribution_keyword(&self, name: &str, implementation: Box<dyn IContributionKeywordImpl>) {
        self.services.contribution_keyword_manager.read().implement_contribution_keyword(name, implementation);
    }

    pub fn add_disposer(&self, disposer: Disposer) {
        self.disposers.write().push(disposer);
    }

    pub fn add_contributes(&self, keyword: &str, definitions: Vec<ContributeDefinition>) {
        let event = DynamicContributesAddedEvent {
            contributor_name: self.plugin_name.clone(),
            keyword: keyword.to_string(),
            definitions,
        };
        self.services.event_manager.read().dispatch(&event);
    }
}
